Introduction
This project demonstrates how to create an Analog Light Dimmer using the ESP32. By rotating a simple potentiometer (a variable resistor), you will directly control the brightness of an LED. This is a fundamental lesson in Pulse Width Modulation (PWM), showing how the ESP32 translates a continuous physical input (turning the knob) into a variable digital output (light intensity).
Key Concepts
- Analog-to-Digital Conversion (ADC): Reading the continuous voltage from the potentiometer.
- Pulse Width Modulation (PWM): The technique used by the ESP32 to fake an analog voltage output on a digital pin, controlling the LED’s brightness.
- Mapping: Converting a large range of input values (0-4095) into a smaller output range (0-255).
Circuit Connections

Components | ESP32 Dev Module |
Potentiometer | GPIO34 |
LED (Red) | GPIO16 |
IMPORTANT: The ESP32’s ADC is most stable on the dedicated input-only pins like GPIO 34-39. Using GPIO 34 for the potentiometer is highly recommended.
Logic Flow
- The user rotates the potentiometer knob.
- The rotation changes the voltage on the middle pin of the potentiometer.
- The ESP32 uses
analogRead(34)
to convert the voltage into a digital value (ranging from 0 to 4095). - The
map() function
scales this 0-4095 range to the 0-255 range required for PWM brightness. - The
ledcWrite() function
sends the new brightness value (duty cycle) to GPIO 16. The LED changes brightness proportionally, providing immediate visual feedback.
Code Lab
Step 1: Install Libraries
No external libraries are required! The ESP32’s functions for ADC (analogRead()
) and PWM (ledcSetup()
, ledcWrite()
) are built-in features of the ESP32 core in the Arduino IDE.
Step 2: Copy and Paste Code
Copy and paste this code into the Arduino IDE:
const int potentiometerPin = 34; // Analog input for the Potentiometer (ADC Pin)
const int ledPin = 16; // Digital output for the LED (PWM Pin)
const int ledChannel = 0; // Use PWM channel 0
const int freq = 5000; // Set frequency to 5 KHz for stable operation
const int resolution = 8; // 8-bit resolution gives a duty cycle range of 0-255
void setup() {
Serial.begin(115200);
// ledcSetup(channel, frequency, resolution_bits)
ledcSetup(ledChannel, freq, resolution);
// ledcAttachPin(pin, channel)
ledcAttachPin(ledPin, ledChannel);
Serial.println("Analog Light Dimmer System Initialized!");
}
void loop() {
// Read the analog value(Range: 0 to 4095)
int potValue = analogRead(potentiometerPin);
// 2. Map the large input range (0-4095) to the smaller PWM output range (0-255)
int brightness = map(potValue, 0, 4095, 0, 255);
// 3. Write the new brightness (duty cycle) to the LED pin
// ledcWrite(channel, duty_cycle_value)
ledcWrite(ledChannel, brightness);
// Output values for verification
Serial.print("Raw ADC Value: ");
Serial.print(potValue);
Serial.print(" -> PWM Brightness: ");
Serial.println(brightness);
delay(10); // Small delay for stable readings and output
}
Step 3: Upload and Run
- Connect your ESP32 board and ensure the correct board and port are selected.
- Upload the code.
- Open the Serial Monitor at 115200 baud.
- Rotate the potentiometer knob and observe the LED and the serial output.
System Check
When working properly:
- The Serial Monitor displays
Raw ADC Value
changing from near 0 to 4095 as you rotate the knob. - The
PWM Brightness
value changes from near 0 to 255. - The LED smoothly dims (turns off) when the knob is at one extreme.
- The LED reaches full brightness when the knob is at the opposite extreme.
Troubleshooting Guide
Problem | Observation | Solution |
LED is always ON or OFF | The LED does not dim or change state, regardless of the knob position. | Check LED Pin: Ensure the LED is connected to GPIO 16. Verify PWM Setup: Confirm ledcSetup and ledcAttachPin are called correctly in setup() . |
Potentiometer reading is stuck | Raw ADC Value is always 0 or 4095 in the Serial Monitor. | Check Wiring: Verify the middle pin is on GPIO 34. |
Light is erratic/flickers | The LED brightness is unstable, or the values jump. | Try increasing the delay(10) to 50 or 100 milliseconds. |
Code fails to upload | Errors related to the ESP32 board or port selection. | Board Selection: Confirm the ESP32 Dev Module is selected under Tools > Board. Check the Tools > Port setting. |
How It Works
Analog-to-Digital Conversion (ADC)
The potentiometer acts as a voltage divider, providing a voltage between 0 and 3.3 (the ESP32’s operating voltage). The ESP32’s ADC reads this voltage and converts it into a digital number. The default resolution is 12-bit, resulting in a value range from 0 to 4095.
PWM and the map()
Function
- The PWM technique rapidly switches the LED pin ON and OFF. The duty cycle (percentage of time spent ON) determines the apparent brightness.
- We set the PWM resolution to 8-bit in
ledcSetup()
, giving us a final duty cycle range of 0 to 255. - The
map(potValue, 0, 4095, 0, 255)
function linearly scales the raw 0-4095 input from the pot down to the $0-255$ output required for the LED’s brightness, making the knob control perfectly smooth.
LEDC API
The ESP32 uses the ledc
functions (ledcSetup
, ledcAttachPin
, ledcWrite
) because it has a dedicated hardware PWM controller that is more powerful and stable than the simple analogWrite()
found on basic Arduino boards. This provides reliable dimming without tying up the main processor.